home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / guile / 1.8 / srfi / srfi-19.scm < prev    next >
Encoding:
Text File  |  2008-12-17  |  58.1 KB  |  1,537 lines

  1. ;;; srfi-19.scm --- Time/Date Library
  2.  
  3. ;;     Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc.
  4. ;;
  5. ;; This library is free software; you can redistribute it and/or
  6. ;; modify it under the terms of the GNU Lesser General Public
  7. ;; License as published by the Free Software Foundation; either
  8. ;; version 2.1 of the License, or (at your option) any later version.
  9. ;; 
  10. ;; This library is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13. ;; Lesser General Public License for more details.
  14. ;; 
  15. ;; You should have received a copy of the GNU Lesser General Public
  16. ;; License along with this library; if not, write to the Free Software
  17. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18.  
  19. ;;; Author: Rob Browning <rlb@cs.utexas.edu>
  20. ;;;         Originally from SRFI reference implementation by Will Fitzgerald.
  21.  
  22. ;;; Commentary:
  23.  
  24. ;; This module is fully documented in the Guile Reference Manual.
  25.  
  26. ;;; Code:
  27.  
  28. ;; FIXME: I haven't checked a decent amount of this code for potential
  29. ;; performance improvements, but I suspect that there may be some
  30. ;; substantial ones to be realized, esp. in the later "parsing" half
  31. ;; of the file, by rewriting the code with use of more Guile native
  32. ;; functions that do more work in a "chunk".
  33. ;;
  34. ;; FIXME: mkoeppe: Time zones are treated a little simplistic in
  35. ;; SRFI-19; they are only a numeric offset.  Thus, printing time zones
  36. ;; (PRIV:LOCALE-PRINT-TIME-ZONE) can't be implemented sensibly.  The
  37. ;; functions taking an optional TZ-OFFSET should be extended to take a
  38. ;; symbolic time-zone (like "CET"); this string should be stored in
  39. ;; the DATE structure.
  40.  
  41. (define-module (srfi srfi-19)
  42.   :use-module (srfi srfi-6)
  43.   :use-module (srfi srfi-8)
  44.   :use-module (srfi srfi-9))
  45.  
  46. (begin-deprecated
  47.  ;; Prevent `export' from re-exporting core bindings.  This behaviour
  48.  ;; of `export' is deprecated and will disappear in one of the next
  49.  ;; releases.
  50.  (define current-time #f))
  51.  
  52. (export ;; Constants
  53.            time-duration
  54.            time-monotonic
  55.            time-process
  56.            time-tai
  57.            time-thread
  58.            time-utc
  59.            ;; Current time and clock resolution
  60.            current-date
  61.            current-julian-day
  62.            current-modified-julian-day
  63.            current-time
  64.            time-resolution
  65.            ;; Time object and accessors
  66.            make-time
  67.            time?
  68.            time-type
  69.            time-nanosecond
  70.            time-second
  71.            set-time-type!
  72.            set-time-nanosecond!
  73.            set-time-second!
  74.            copy-time
  75.            ;; Time comparison procedures
  76.            time<=?
  77.            time<?
  78.            time=?
  79.            time>=?
  80.            time>?
  81.            ;; Time arithmetic procedures
  82.            time-difference
  83.            time-difference!
  84.            add-duration
  85.            add-duration!
  86.            subtract-duration
  87.            subtract-duration!
  88.            ;; Date object and accessors
  89.            make-date
  90.            date?
  91.            date-nanosecond
  92.            date-second
  93.            date-minute
  94.            date-hour
  95.            date-day
  96.            date-month
  97.            date-year
  98.            date-zone-offset
  99.            date-year-day
  100.            date-week-day
  101.            date-week-number
  102.            ;; Time/Date/Julian Day/Modified Julian Day converters
  103.            date->julian-day
  104.            date->modified-julian-day
  105.            date->time-monotonic
  106.            date->time-tai
  107.            date->time-utc
  108.            julian-day->date
  109.            julian-day->time-monotonic
  110.            julian-day->time-tai
  111.            julian-day->time-utc
  112.            modified-julian-day->date
  113.            modified-julian-day->time-monotonic
  114.            modified-julian-day->time-tai
  115.            modified-julian-day->time-utc
  116.            time-monotonic->date
  117.            time-monotonic->time-tai
  118.            time-monotonic->time-tai!
  119.            time-monotonic->time-utc
  120.            time-monotonic->time-utc!
  121.            time-tai->date
  122.            time-tai->julian-day
  123.            time-tai->modified-julian-day
  124.            time-tai->time-monotonic
  125.            time-tai->time-monotonic!
  126.            time-tai->time-utc
  127.            time-tai->time-utc!
  128.            time-utc->date
  129.            time-utc->julian-day
  130.            time-utc->modified-julian-day
  131.            time-utc->time-monotonic
  132.            time-utc->time-monotonic!
  133.            time-utc->time-tai
  134.            time-utc->time-tai!
  135.            ;; Date to string/string to date converters.
  136.            date->string
  137.            string->date)
  138.  
  139. (cond-expand-provide (current-module) '(srfi-19))
  140.  
  141. (define time-tai 'time-tai)
  142. (define time-utc 'time-utc)
  143. (define time-monotonic 'time-monotonic)
  144. (define time-thread 'time-thread)
  145. (define time-process 'time-process)
  146. (define time-duration 'time-duration)
  147.  
  148. ;; FIXME: do we want to add gc time?
  149. ;; (define time-gc 'time-gc)
  150.  
  151. ;;-- LOCALE dependent constants
  152.  
  153. (define priv:locale-number-separator ".")
  154.  
  155. (define priv:locale-abbr-weekday-vector
  156.   (vector "Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat"))
  157.  
  158. (define priv:locale-long-weekday-vector
  159.   (vector
  160.    "Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"))
  161.  
  162. ;; note empty string in 0th place.
  163. (define priv:locale-abbr-month-vector
  164.   (vector ""
  165.           "Jan"
  166.           "Feb"
  167.           "Mar"
  168.           "Apr"
  169.           "May"
  170.           "Jun"
  171.           "Jul"
  172.           "Aug"
  173.           "Sep"
  174.           "Oct"
  175.           "Nov"
  176.           "Dec"))
  177.  
  178. (define priv:locale-long-month-vector
  179.   (vector ""
  180.           "January"
  181.           "February"
  182.           "March"
  183.           "April"
  184.           "May"
  185.           "June"
  186.           "July"
  187.           "August"
  188.           "September"
  189.           "October"
  190.           "November"
  191.           "December"))
  192.  
  193. (define priv:locale-pm "PM")
  194. (define priv:locale-am "AM")
  195.  
  196. ;; See date->string
  197. (define priv:locale-date-time-format "~a ~b ~d ~H:~M:~S~z ~Y")
  198. (define priv:locale-short-date-format "~m/~d/~y")
  199. (define priv:locale-time-format "~H:~M:~S")
  200. (define priv:iso-8601-date-time-format "~Y-~m-~dT~H:~M:~S~z")
  201.  
  202. ;;-- Miscellaneous Constants.
  203. ;;-- only the priv:tai-epoch-in-jd might need changing if
  204. ;;   a different epoch is used.
  205.  
  206. (define priv:nano 1000000000)           ; nanoseconds in a second
  207. (define priv:sid  86400)                ; seconds in a day
  208. (define priv:sihd 43200)                ; seconds in a half day
  209. (define priv:tai-epoch-in-jd 4881175/2) ; julian day number for 'the epoch'
  210.  
  211. ;; FIXME: should this be something other than misc-error?
  212. (define (priv:time-error caller type value)
  213.   (if value
  214.       (throw 'misc-error caller "TIME-ERROR type ~A: ~S" (list type value) #f)
  215.       (throw 'misc-error caller "TIME-ERROR type ~A" (list type) #f)))
  216.  
  217. ;; A table of leap seconds
  218. ;; See ftp://maia.usno.navy.mil/ser7/tai-utc.dat
  219. ;; and update as necessary.
  220. ;; this procedures reads the file in the abover
  221. ;; format and creates the leap second table
  222. ;; it also calls the almost standard, but not R5 procedures read-line
  223. ;; & open-input-string
  224. ;; ie (set! priv:leap-second-table (priv:read-tai-utc-date "tai-utc.dat"))
  225.  
  226. (define (priv:read-tai-utc-data filename)
  227.   (define (convert-jd jd)
  228.     (* (- (inexact->exact jd) priv:tai-epoch-in-jd) priv:sid))
  229.   (define (convert-sec sec)
  230.     (inexact->exact sec))
  231.   (let ((port (open-input-file filename))
  232.         (table '()))
  233.     (let loop ((line (read-line port)))
  234.       (if (not (eq? line eof))
  235.           (begin
  236.             (let* ((data (read (open-input-string
  237.                                 (string-append "(" line ")"))))
  238.                    (year (car data))
  239.                    (jd   (cadddr (cdr data)))
  240.                    (secs (cadddr (cdddr data))))
  241.               (if (>= year 1972)
  242.                   (set! table (cons
  243.                                (cons (convert-jd jd) (convert-sec secs))
  244.                                table)))
  245.               (loop (read-line port))))))
  246.     table))
  247.  
  248. ;; each entry is (tai seconds since epoch . # seconds to subtract for utc)
  249. ;; note they go higher to lower, and end in 1972.
  250. (define priv:leap-second-table
  251.   '((1136073600 . 33)
  252.     (915148800 . 32)
  253.     (867715200 . 31)
  254.     (820454400 . 30)
  255.     (773020800 . 29)
  256.     (741484800 . 28)
  257.     (709948800 . 27)
  258.     (662688000 . 26)
  259.     (631152000 . 25)
  260.     (567993600 . 24)
  261.     (489024000 . 23)
  262.     (425865600 . 22)
  263.     (394329600 . 21)
  264.     (362793600 . 20)
  265.     (315532800 . 19)
  266.     (283996800 . 18)
  267.     (252460800 . 17)
  268.     (220924800 . 16)
  269.     (189302400 . 15)
  270.     (157766400 . 14)
  271.     (126230400 . 13)
  272.     (94694400  . 12)
  273.     (78796800  . 11)
  274.     (63072000  . 10)))
  275.  
  276. (define (read-leap-second-table filename)
  277.   (set! priv:leap-second-table (priv:read-tai-utc-data filename))
  278.   (values))
  279.  
  280.  
  281. (define (priv:leap-second-delta utc-seconds)
  282.   (letrec ((lsd (lambda (table)
  283.                   (cond ((>= utc-seconds (caar table))
  284.                          (cdar table))
  285.                         (else (lsd (cdr table)))))))
  286.     (if (< utc-seconds  (* (- 1972 1970) 365 priv:sid)) 0
  287.         (lsd  priv:leap-second-table))))
  288.  
  289.  
  290. ;;; the TIME structure; creates the accessors, too.
  291.  
  292. (define-record-type time
  293.   (make-time-unnormalized type nanosecond second)
  294.   time?
  295.   (type time-type set-time-type!)
  296.   (nanosecond time-nanosecond set-time-nanosecond!)
  297.   (second time-second set-time-second!))
  298.  
  299. (define (copy-time time)
  300.   (make-time (time-type time) (time-nanosecond time) (time-second time)))
  301.  
  302. (define (priv:split-real r)
  303.   (if (integer? r)
  304.       (values (inexact->exact r) 0)
  305.       (let ((l (truncate r)))
  306.         (values (inexact->exact l) (- r l)))))
  307.  
  308. (define (priv:time-normalize! t)
  309.   (if (>= (abs (time-nanosecond t)) 1000000000)
  310.       (receive (int frac)
  311.       (priv:split-real (time-nanosecond t))
  312.     (set-time-second! t (+ (time-second t)
  313.                    (quotient int 1000000000)))
  314.     (set-time-nanosecond! t (+ (remainder int 1000000000)
  315.                    frac))))
  316.   (if (and (positive? (time-second t))
  317.            (negative? (time-nanosecond t)))
  318.       (begin
  319.         (set-time-second! t (- (time-second t) 1))
  320.         (set-time-nanosecond! t (+ 1000000000 (time-nanosecond t))))
  321.       (if (and (negative? (time-second t))
  322.                (positive? (time-nanosecond t)))
  323.           (begin
  324.             (set-time-second! t (+ (time-second t) 1))
  325.             (set-time-nanosecond! t (+ 1000000000 (time-nanosecond t))))))
  326.   t)
  327.  
  328. (define (make-time type nanosecond second)
  329.   (priv:time-normalize! (make-time-unnormalized type nanosecond second)))
  330.  
  331. ;; Helpers
  332. ;; FIXME: finish this and publish it?
  333. (define (date->broken-down-time date)
  334.   (let ((result (mktime 0)))
  335.     ;; FIXME: What should we do about leap-seconds which may overflow
  336.     ;; set-tm:sec?
  337.     (set-tm:sec result (date-second date))
  338.     (set-tm:min result (date-minute date))
  339.     (set-tm:hour result (date-hour date))
  340.     ;; FIXME: SRFI day ranges from 0-31.  (not compatible with set-tm:mday).
  341.     (set-tm:mday result (date-day date))
  342.     (set-tm:month result (- (date-month date) 1))
  343.     ;; FIXME: need to signal error on range violation.
  344.     (set-tm:year result (+ 1900 (date-year date)))
  345.     (set-tm:isdst result -1)
  346.     (set-tm:gmtoff result (- (date-zone-offset date)))
  347.     result))
  348.  
  349. ;;; current-time
  350.  
  351. ;;; specific time getters.
  352.  
  353. (define (priv:current-time-utc)
  354.   ;; Resolution is microseconds.
  355.   (let ((tod (gettimeofday)))
  356.     (make-time time-utc (* (cdr tod) 1000) (car tod))))
  357.  
  358. (define (priv:current-time-tai)
  359.   ;; Resolution is microseconds.
  360.   (let* ((tod (gettimeofday))
  361.          (sec (car tod))
  362.          (usec (cdr tod)))
  363.     (make-time time-tai
  364.                (* usec 1000)
  365.                (+ (car tod) (priv:leap-second-delta sec)))))
  366.  
  367. ;;(define (priv:current-time-ms-time time-type proc)
  368. ;;  (let ((current-ms (proc)))
  369. ;;    (make-time time-type
  370. ;;               (quotient current-ms 10000)
  371. ;;       (* (remainder current-ms 1000) 10000))))
  372.  
  373. ;; -- we define it to be the same as TAI.
  374. ;;    A different implemation of current-time-montonic
  375. ;;    will require rewriting all of the time-monotonic converters,
  376. ;;    of course.
  377.  
  378. (define (priv:current-time-monotonic)
  379.   ;; Resolution is microseconds.
  380.   (priv:current-time-tai))
  381.  
  382. (define (priv:current-time-thread)
  383.   (priv:time-error 'current-time 'unsupported-clock-type 'time-thread))
  384.  
  385. (define priv:ns-per-guile-tick (/ 1000000000 internal-time-units-per-second))
  386.  
  387. (define (priv:current-time-process)
  388.   (let ((run-time (get-internal-run-time)))
  389.     (make-time
  390.      time-process
  391.      (* (remainder run-time internal-time-units-per-second)
  392.         priv:ns-per-guile-tick)
  393.      (quotient run-time internal-time-units-per-second))))
  394.  
  395. ;;(define (priv:current-time-gc)
  396. ;;  (priv:current-time-ms-time time-gc current-gc-milliseconds))
  397.  
  398. (define (current-time . clock-type)
  399.   (let ((clock-type (if (null? clock-type) time-utc (car clock-type))))
  400.     (cond
  401.      ((eq? clock-type time-tai) (priv:current-time-tai))
  402.      ((eq? clock-type time-utc) (priv:current-time-utc))
  403.      ((eq? clock-type time-monotonic) (priv:current-time-monotonic))
  404.      ((eq? clock-type time-thread) (priv:current-time-thread))
  405.      ((eq? clock-type time-process) (priv:current-time-process))
  406.      ;;     ((eq? clock-type time-gc) (priv:current-time-gc))
  407.      (else (priv:time-error 'current-time 'invalid-clock-type clock-type)))))
  408.  
  409. ;; -- Time Resolution
  410. ;; This is the resolution of the clock in nanoseconds.
  411. ;; This will be implementation specific.
  412.  
  413. (define (time-resolution . clock-type)
  414.   (let ((clock-type (if (null? clock-type) time-utc (car clock-type))))
  415.     (case clock-type
  416.       ((time-tai) 1000)
  417.       ((time-utc) 1000)
  418.       ((time-monotonic) 1000)
  419.       ((time-process) priv:ns-per-guile-tick)
  420.       ;;     ((eq? clock-type time-thread) 1000)
  421.       ;;     ((eq? clock-type time-gc) 10000)
  422.       (else (priv:time-error 'time-resolution 'invalid-clock-type clock-type)))))
  423.  
  424. ;; -- Time comparisons
  425.  
  426. (define (time=? t1 t2)
  427.   ;; Arrange tests for speed and presume that t1 and t2 are actually times.
  428.   ;; also presume it will be rare to check two times of different types.
  429.   (and (= (time-second t1) (time-second t2))
  430.        (= (time-nanosecond t1) (time-nanosecond t2))
  431.        (eq? (time-type t1) (time-type t2))))
  432.  
  433. (define (time>? t1 t2)
  434.   (or (> (time-second t1) (time-second t2))
  435.       (and (= (time-second t1) (time-second t2))
  436.            (> (time-nanosecond t1) (time-nanosecond t2)))))
  437.  
  438. (define (time<? t1 t2)
  439.   (or (< (time-second t1) (time-second t2))
  440.       (and (= (time-second t1) (time-second t2))
  441.            (< (time-nanosecond t1) (time-nanosecond t2)))))
  442.  
  443. (define (time>=? t1 t2)
  444.   (or (> (time-second t1) (time-second t2))
  445.       (and (= (time-second t1) (time-second t2))
  446.            (>= (time-nanosecond t1) (time-nanosecond t2)))))
  447.  
  448. (define (time<=? t1 t2)
  449.   (or (< (time-second t1) (time-second t2))
  450.       (and (= (time-second t1) (time-second t2))
  451.            (<= (time-nanosecond t1) (time-nanosecond t2)))))
  452.  
  453. ;; -- Time arithmetic
  454.  
  455. (define (time-difference! time1 time2)
  456.   (let ((sec-diff (- (time-second time1) (time-second time2)))
  457.         (nsec-diff (- (time-nanosecond time1) (time-nanosecond time2))))
  458.     (set-time-type! time1 time-duration)
  459.     (set-time-second! time1 sec-diff)
  460.     (set-time-nanosecond! time1 nsec-diff)
  461.     (priv:time-normalize! time1)))
  462.  
  463. (define (time-difference time1 time2)
  464.   (let ((result (copy-time time1)))
  465.     (time-difference! result time2)))
  466.  
  467. (define (add-duration! t duration)
  468.   (if (not (eq? (time-type duration) time-duration))
  469.       (priv:time-error 'add-duration 'not-duration duration)
  470.       (let ((sec-plus (+ (time-second t) (time-second duration)))
  471.             (nsec-plus (+ (time-nanosecond t) (time-nanosecond duration))))
  472.         (set-time-second! t sec-plus)
  473.         (set-time-nanosecond! t nsec-plus)
  474.         (priv:time-normalize! t))))
  475.  
  476. (define (add-duration t duration)
  477.   (let ((result (copy-time t)))
  478.     (add-duration! result duration)))
  479.  
  480. (define (subtract-duration! t duration)
  481.   (if (not (eq? (time-type duration) time-duration))
  482.       (priv:time-error 'add-duration 'not-duration duration)
  483.       (let ((sec-minus  (- (time-second t) (time-second duration)))
  484.             (nsec-minus (- (time-nanosecond t) (time-nanosecond duration))))
  485.         (set-time-second! t sec-minus)
  486.         (set-time-nanosecond! t nsec-minus)
  487.         (priv:time-normalize! t))))
  488.  
  489. (define (subtract-duration time1 duration)
  490.   (let ((result (copy-time time1)))
  491.     (subtract-duration! result duration)))
  492.  
  493. ;; -- Converters between types.
  494.  
  495. (define (priv:time-tai->time-utc! time-in time-out caller)
  496.   (if (not (eq? (time-type time-in) time-tai))
  497.       (priv:time-error caller 'incompatible-time-types time-in))
  498.   (set-time-type! time-out time-utc)
  499.   (set-time-nanosecond! time-out (time-nanosecond time-in))
  500.   (set-time-second!     time-out (- (time-second time-in)
  501.                                     (priv:leap-second-delta
  502.                                      (time-second time-in))))
  503.   time-out)
  504.  
  505. (define (time-tai->time-utc time-in)
  506.   (priv:time-tai->time-utc! time-in (make-time-unnormalized #f #f #f) 'time-tai->time-utc))
  507.  
  508.  
  509. (define (time-tai->time-utc! time-in)
  510.   (priv:time-tai->time-utc! time-in time-in 'time-tai->time-utc!))
  511.  
  512. (define (priv:time-utc->time-tai! time-in time-out caller)
  513.   (if (not (eq? (time-type time-in) time-utc))
  514.       (priv:time-error caller 'incompatible-time-types time-in))
  515.   (set-time-type! time-out time-tai)
  516.   (set-time-nanosecond! time-out (time-nanosecond time-in))
  517.   (set-time-second!     time-out (+ (time-second time-in)
  518.                                     (priv:leap-second-delta
  519.                                      (time-second time-in))))
  520.   time-out)
  521.  
  522. (define (time-utc->time-tai time-in)
  523.   (priv:time-utc->time-tai! time-in (make-time-unnormalized #f #f #f) 'time-utc->time-tai))
  524.  
  525. (define (time-utc->time-tai! time-in)
  526.   (priv:time-utc->time-tai! time-in time-in 'time-utc->time-tai!))
  527.  
  528. ;; -- these depend on time-monotonic having the same definition as time-tai!
  529. (define (time-monotonic->time-utc time-in)
  530.   (if (not (eq? (time-type time-in) time-monotonic))
  531.       (priv:time-error caller 'incompatible-time-types time-in))
  532.   (let ((ntime (copy-time time-in)))
  533.     (set-time-type! ntime time-tai)
  534.     (priv:time-tai->time-utc! ntime ntime 'time-monotonic->time-utc)))
  535.  
  536. (define (time-monotonic->time-utc! time-in)
  537.   (if (not (eq? (time-type time-in) time-monotonic))
  538.       (priv:time-error caller 'incompatible-time-types time-in))
  539.   (set-time-type! time-in time-tai)
  540.   (priv:time-tai->time-utc! ntime ntime 'time-monotonic->time-utc))
  541.  
  542. (define (time-monotonic->time-tai time-in)
  543.   (if (not (eq? (time-type time-in) time-monotonic))
  544.       (priv:time-error caller 'incompatible-time-types time-in))
  545.   (let ((ntime (copy-time time-in)))
  546.     (set-time-type! ntime time-tai)
  547.     ntime))
  548.  
  549. (define (time-monotonic->time-tai! time-in)
  550.   (if (not (eq? (time-type time-in) time-monotonic))
  551.       (priv:time-error caller 'incompatible-time-types time-in))
  552.   (set-time-type! time-in time-tai)
  553.   time-in)
  554.  
  555. (define (time-utc->time-monotonic time-in)
  556.   (if (not (eq? (time-type time-in) time-utc))
  557.       (priv:time-error caller 'incompatible-time-types time-in))
  558.   (let ((ntime (priv:time-utc->time-tai! time-in (make-time-unnormalized #f #f #f)
  559.                                          'time-utc->time-monotonic)))
  560.     (set-time-type! ntime time-monotonic)
  561.     ntime))
  562.  
  563. (define (time-utc->time-monotonic! time-in)
  564.   (if (not (eq? (time-type time-in) time-utc))
  565.       (priv:time-error caller 'incompatible-time-types time-in))
  566.   (let ((ntime (priv:time-utc->time-tai! time-in time-in
  567.                                          'time-utc->time-monotonic!)))
  568.     (set-time-type! ntime time-monotonic)
  569.     ntime))
  570.  
  571. (define (time-tai->time-monotonic time-in)
  572.   (if (not (eq? (time-type time-in) time-tai))
  573.       (priv:time-error caller 'incompatible-time-types time-in))
  574.   (let ((ntime (copy-time time-in)))
  575.     (set-time-type! ntime time-monotonic)
  576.     ntime))
  577.  
  578. (define (time-tai->time-monotonic! time-in)
  579.   (if (not (eq? (time-type time-in) time-tai))
  580.       (priv:time-error caller 'incompatible-time-types time-in))
  581.   (set-time-type! time-in time-monotonic)
  582.   time-in)
  583.  
  584. ;; -- Date Structures
  585.  
  586. ;; FIXME: to be really safe, perhaps we should normalize the
  587. ;; seconds/nanoseconds/minutes coming in to make-date...
  588.  
  589. (define-record-type date
  590.   (make-date nanosecond second minute
  591.              hour day month
  592.              year
  593.              zone-offset)
  594.   date?
  595.   (nanosecond date-nanosecond set-date-nanosecond!)
  596.   (second date-second set-date-second!)
  597.   (minute date-minute set-date-minute!)
  598.   (hour date-hour set-date-hour!)
  599.   (day date-day set-date-day!)
  600.   (month date-month set-date-month!)
  601.   (year date-year set-date-year!)
  602.   (zone-offset date-zone-offset set-date-zone-offset!))
  603.  
  604. ;; gives the julian day which starts at noon.
  605. (define (priv:encode-julian-day-number day month year)
  606.   (let* ((a (quotient (- 14 month) 12))
  607.          (y (- (+ year 4800) a (if (negative? year) -1  0)))
  608.          (m (- (+ month (* 12 a)) 3)))
  609.     (+ day
  610.        (quotient (+ (* 153 m) 2) 5)
  611.        (* 365 y)
  612.        (quotient y 4)
  613.        (- (quotient y 100))
  614.        (quotient y 400)
  615.        -32045)))
  616.  
  617. ;; gives the seconds/date/month/year
  618. (define (priv:decode-julian-day-number jdn)
  619.   (let* ((days (inexact->exact (truncate jdn)))
  620.          (a (+ days 32044))
  621.          (b (quotient (+ (* 4 a) 3) 146097))
  622.          (c (- a (quotient (* 146097 b) 4)))
  623.          (d (quotient (+ (* 4 c) 3) 1461))
  624.          (e (- c (quotient (* 1461 d) 4)))
  625.          (m (quotient (+ (* 5 e) 2) 153))
  626.          (y (+ (* 100 b) d -4800 (quotient m 10))))
  627.     (values ; seconds date month year
  628.      (* (- jdn days) priv:sid)
  629.      (+ e (- (quotient (+ (* 153 m) 2) 5)) 1)
  630.      (+ m 3 (* -12 (quotient m 10)))
  631.      (if (>= 0 y) (- y 1) y))))
  632.  
  633. ;; relies on the fact that we named our time zone accessor
  634. ;; differently from MzScheme's....
  635. ;; This should be written to be OS specific.
  636.  
  637. (define (priv:local-tz-offset utc-time)
  638.   ;; SRFI uses seconds West, but guile (and libc) use seconds East.
  639.   (- (tm:gmtoff (localtime (time-second utc-time)))))
  640.  
  641. ;; special thing -- ignores nanos
  642. (define (priv:time->julian-day-number seconds tz-offset)
  643.   (+ (/ (+ seconds tz-offset priv:sihd)
  644.         priv:sid)
  645.      priv:tai-epoch-in-jd))
  646.  
  647. (define (priv:leap-second? second)
  648.   (and (assoc second priv:leap-second-table) #t))
  649.  
  650. (define (time-utc->date time . tz-offset)
  651.   (if (not (eq? (time-type time) time-utc))
  652.       (priv:time-error 'time->date 'incompatible-time-types  time))
  653.   (let* ((offset (if (null? tz-offset)
  654.              (priv:local-tz-offset time)
  655.              (car tz-offset)))
  656.          (leap-second? (priv:leap-second? (+ offset (time-second time))))
  657.          (jdn (priv:time->julian-day-number (if leap-second?
  658.                                                 (- (time-second time) 1)
  659.                                                 (time-second time))
  660.                                             offset)))
  661.  
  662.     (call-with-values (lambda () (priv:decode-julian-day-number jdn))
  663.       (lambda (secs date month year)
  664.     ;; secs is a real because jdn is a real in Guile;
  665.     ;; but it is conceptionally an integer.
  666.         (let* ((int-secs (inexact->exact (round secs)))
  667.                (hours    (quotient int-secs (* 60 60)))
  668.                (rem      (remainder int-secs (* 60 60)))
  669.                (minutes  (quotient rem 60))
  670.                (seconds  (remainder rem 60)))
  671.           (make-date (time-nanosecond time)
  672.                      (if leap-second? (+ seconds 1) seconds)
  673.                      minutes
  674.                      hours
  675.                      date
  676.                      month
  677.                      year
  678.                      offset))))))
  679.  
  680. (define (time-tai->date time  . tz-offset)
  681.   (if (not (eq? (time-type time) time-tai))
  682.       (priv:time-error 'time->date 'incompatible-time-types  time))
  683.   (let* ((offset (if (null? tz-offset)
  684.              (priv:local-tz-offset (time-tai->time-utc time))
  685.              (car tz-offset)))
  686.          (seconds (- (time-second time)
  687.                      (priv:leap-second-delta (time-second time))))
  688.          (leap-second? (priv:leap-second? (+ offset seconds)))
  689.          (jdn (priv:time->julian-day-number (if leap-second?
  690.                                                 (- seconds 1)
  691.                                                 seconds)
  692.                                             offset)))
  693.     (call-with-values (lambda () (priv:decode-julian-day-number jdn))
  694.       (lambda (secs date month year)
  695.     ;; secs is a real because jdn is a real in Guile;
  696.     ;; but it is conceptionally an integer.
  697.         ;; adjust for leap seconds if necessary ...
  698.         (let* ((int-secs (inexact->exact (round secs)))
  699.            (hours    (quotient int-secs (* 60 60)))
  700.                (rem      (remainder int-secs (* 60 60)))
  701.                (minutes  (quotient rem 60))
  702.                (seconds  (remainder rem 60)))
  703.           (make-date (time-nanosecond time)
  704.                      (if leap-second? (+ seconds 1) seconds)
  705.                      minutes
  706.                      hours
  707.                      date
  708.                      month
  709.                      year
  710.                      offset))))))
  711.  
  712. ;; this is the same as time-tai->date.
  713. (define (time-monotonic->date time . tz-offset)
  714.   (if (not (eq? (time-type time) time-monotonic))
  715.       (priv:time-error 'time->date 'incompatible-time-types  time))
  716.   (let* ((offset (if (null? tz-offset)
  717.              (priv:local-tz-offset (time-monotonic->time-utc time))
  718.              (car tz-offset)))
  719.          (seconds (- (time-second time)
  720.                      (priv:leap-second-delta (time-second time))))
  721.          (leap-second? (priv:leap-second? (+ offset seconds)))
  722.          (jdn (priv:time->julian-day-number (if leap-second?
  723.                                                 (- seconds 1)
  724.                                                 seconds)
  725.                                             offset)))
  726.     (call-with-values (lambda () (priv:decode-julian-day-number jdn))
  727.       (lambda (secs date month year)
  728.     ;; secs is a real because jdn is a real in Guile;
  729.     ;; but it is conceptionally an integer.
  730.         ;; adjust for leap seconds if necessary ...
  731.         (let* ((int-secs (inexact->exact (round secs)))
  732.            (hours    (quotient int-secs (* 60 60)))
  733.                (rem      (remainder int-secs (* 60 60)))
  734.                (minutes  (quotient rem 60))
  735.                (seconds  (remainder rem 60)))
  736.           (make-date (time-nanosecond time)
  737.                      (if leap-second? (+ seconds 1) seconds)
  738.                      minutes
  739.                      hours
  740.                      date
  741.                      month
  742.                      year
  743.                      offset))))))
  744.  
  745. (define (date->time-utc date)
  746.   (let* ((jdays (- (priv:encode-julian-day-number (date-day date)
  747.                                                  (date-month date)
  748.                                                  (date-year date))
  749.            priv:tai-epoch-in-jd))
  750.      ;; jdays is an integer plus 1/2,
  751.      (jdays-1/2 (inexact->exact (- jdays 1/2))))
  752.     (make-time
  753.      time-utc
  754.      (date-nanosecond date)
  755.      (+ (* jdays-1/2 24 60 60)
  756.         (* (date-hour date) 60 60)
  757.         (* (date-minute date) 60)
  758.         (date-second date)
  759.     (- (date-zone-offset date))))))
  760.  
  761. (define (date->time-tai date)
  762.   (time-utc->time-tai! (date->time-utc date)))
  763.  
  764. (define (date->time-monotonic date)
  765.   (time-utc->time-monotonic! (date->time-utc date)))
  766.  
  767. (define (priv:leap-year? year)
  768.   (or (= (modulo year 400) 0)
  769.       (and (= (modulo year 4) 0) (not (= (modulo year 100) 0)))))
  770.  
  771. (define (leap-year? date)
  772.   (priv:leap-year? (date-year date)))
  773.  
  774. ;; Map 1-based month number M to number of days in the year before the
  775. ;; start of month M (in a non-leap year).
  776. (define priv:month-assoc '((1 . 0)   (2 . 31)   (3 . 59)   (4 . 90)
  777.                (5 . 120) (6 . 151)  (7 . 181)  (8 . 212)
  778.                (9 . 243) (10 . 273) (11 . 304) (12 . 334)))
  779.  
  780. (define (priv:year-day day month year)
  781.   (let ((days-pr (assoc month priv:month-assoc)))
  782.     (if (not days-pr)
  783.         (priv:error 'date-year-day 'invalid-month-specification month))
  784.     (if (and (priv:leap-year? year) (> month 2))
  785.         (+ day (cdr days-pr) 1)
  786.         (+ day (cdr days-pr)))))
  787.  
  788. (define (date-year-day date)
  789.   (priv:year-day (date-day date) (date-month date) (date-year date)))
  790.  
  791. ;; from calendar faq
  792. (define (priv:week-day day month year)
  793.   (let* ((a (quotient (- 14 month) 12))
  794.          (y (- year a))
  795.          (m (+ month (* 12 a) -2)))
  796.     (modulo (+ day
  797.                y
  798.                (quotient y 4)
  799.                (- (quotient y 100))
  800.                (quotient y 400)
  801.                (quotient (* 31 m) 12))
  802.             7)))
  803.  
  804. (define (date-week-day date)
  805.   (priv:week-day (date-day date) (date-month date) (date-year date)))
  806.  
  807. (define (priv:days-before-first-week date day-of-week-starting-week)
  808.   (let* ((first-day (make-date 0 0 0 0
  809.                                1
  810.                                1
  811.                                (date-year date)
  812.                                #f))
  813.          (fdweek-day (date-week-day first-day)))
  814.     (modulo (- day-of-week-starting-week fdweek-day)
  815.             7)))
  816.  
  817. ;; The "-1" here is a fix for the reference implementation, to make a new
  818. ;; week start on the given day-of-week-starting-week.  date-year-day returns
  819. ;; a day starting from 1 for 1st Jan.
  820. ;;
  821. (define (date-week-number date day-of-week-starting-week)
  822.   (quotient (- (date-year-day date)
  823.            1
  824.                (priv:days-before-first-week  date day-of-week-starting-week))
  825.             7))
  826.  
  827. (define (current-date . tz-offset)
  828.   (let ((time (current-time time-utc)))
  829.     (time-utc->date
  830.      time
  831.      (if (null? tz-offset)
  832.      (priv:local-tz-offset time)
  833.      (car tz-offset)))))
  834.  
  835. ;; given a 'two digit' number, find the year within 50 years +/-
  836. (define (priv:natural-year n)
  837.   (let* ((current-year (date-year (current-date)))
  838.          (current-century (* (quotient current-year 100) 100)))
  839.     (cond
  840.      ((>= n 100) n)
  841.      ((<  n 0) n)
  842.      ((<=  (- (+ current-century n) current-year) 50) (+ current-century n))
  843.      (else (+ (- current-century 100) n)))))
  844.  
  845. (define (date->julian-day date)
  846.   (let ((nanosecond (date-nanosecond date))
  847.         (second (date-second date))
  848.         (minute (date-minute date))
  849.         (hour (date-hour date))
  850.         (day (date-day date))
  851.         (month (date-month date))
  852.         (year (date-year date))
  853.         (offset (date-zone-offset date)))
  854.     (+ (priv:encode-julian-day-number day month year)
  855.        (- 1/2)
  856.        (+ (/ (+ (- offset)
  857.                 (* hour 60 60)
  858.                 (* minute 60)
  859.                 second
  860.                 (/ nanosecond priv:nano))
  861.              priv:sid)))))
  862.  
  863. (define (date->modified-julian-day date)
  864.   (- (date->julian-day date)
  865.      4800001/2))
  866.  
  867. (define (time-utc->julian-day time)
  868.   (if (not (eq? (time-type time) time-utc))
  869.       (priv:time-error 'time->date 'incompatible-time-types  time))
  870.   (+ (/ (+ (time-second time) (/ (time-nanosecond time) priv:nano))
  871.         priv:sid)
  872.      priv:tai-epoch-in-jd))
  873.  
  874. (define (time-utc->modified-julian-day time)
  875.   (- (time-utc->julian-day time)
  876.      4800001/2))
  877.  
  878. (define (time-tai->julian-day time)
  879.   (if (not (eq? (time-type time) time-tai))
  880.       (priv:time-error 'time->date 'incompatible-time-types  time))
  881.   (+ (/ (+ (- (time-second time)
  882.               (priv:leap-second-delta (time-second time)))
  883.            (/ (time-nanosecond time) priv:nano))
  884.         priv:sid)
  885.      priv:tai-epoch-in-jd))
  886.  
  887. (define (time-tai->modified-julian-day time)
  888.   (- (time-tai->julian-day time)
  889.      4800001/2))
  890.  
  891. ;; this is the same as time-tai->julian-day
  892. (define (time-monotonic->julian-day time)
  893.   (if (not (eq? (time-type time) time-monotonic))
  894.       (priv:time-error 'time->date 'incompatible-time-types  time))
  895.   (+ (/ (+ (- (time-second time)
  896.               (priv:leap-second-delta (time-second time)))
  897.            (/ (time-nanosecond time) priv:nano))
  898.         priv:sid)
  899.      priv:tai-epoch-in-jd))
  900.  
  901. (define (time-monotonic->modified-julian-day time)
  902.   (- (time-monotonic->julian-day time)
  903.      4800001/2))
  904.  
  905. (define (julian-day->time-utc jdn)
  906.   (let ((secs (* priv:sid (- jdn priv:tai-epoch-in-jd))))
  907.     (receive (seconds parts)
  908.     (priv:split-real secs)
  909.       (make-time time-utc
  910.          (* parts priv:nano)
  911.          seconds))))
  912.  
  913. (define (julian-day->time-tai jdn)
  914.   (time-utc->time-tai! (julian-day->time-utc jdn)))
  915.  
  916. (define (julian-day->time-monotonic jdn)
  917.   (time-utc->time-monotonic! (julian-day->time-utc jdn)))
  918.  
  919. (define (julian-day->date jdn . tz-offset)
  920.   (let* ((time (julian-day->time-utc jdn))
  921.      (offset (if (null? tz-offset)
  922.              (priv:local-tz-offset time)
  923.              (car tz-offset))))
  924.     (time-utc->date time offset)))
  925.  
  926. (define (modified-julian-day->date jdn . tz-offset)
  927.   (apply julian-day->date (+ jdn 4800001/2)
  928.      tz-offset))
  929.  
  930. (define (modified-julian-day->time-utc jdn)
  931.   (julian-day->time-utc (+ jdn 4800001/2)))
  932.  
  933. (define (modified-julian-day->time-tai jdn)
  934.   (julian-day->time-tai (+ jdn 4800001/2)))
  935.  
  936. (define (modified-julian-day->time-monotonic jdn)
  937.   (julian-day->time-monotonic (+ jdn 4800001/2)))
  938.  
  939. (define (current-julian-day)
  940.   (time-utc->julian-day (current-time time-utc)))
  941.  
  942. (define (current-modified-julian-day)
  943.   (time-utc->modified-julian-day (current-time time-utc)))
  944.  
  945. ;; returns a string rep. of number N, of minimum LENGTH, padded with
  946. ;; character PAD-WITH. If PAD-WITH is #f, no padding is done, and it's
  947. ;; as if number->string was used.  if string is longer than or equal
  948. ;; in length to LENGTH, it's as if number->string was used.
  949.  
  950. (define (priv:padding n pad-with length)
  951.   (let* ((str (number->string n))
  952.          (str-len (string-length str)))
  953.     (if (or (>= str-len length)
  954.             (not pad-with))
  955.         str
  956.         (string-append (make-string (- length str-len) pad-with) str))))
  957.  
  958. (define (priv:last-n-digits i n)
  959.   (abs (remainder i (expt 10 n))))
  960.  
  961. (define (priv:locale-abbr-weekday n)
  962.   (vector-ref priv:locale-abbr-weekday-vector n))
  963.  
  964. (define (priv:locale-long-weekday n)
  965.   (vector-ref priv:locale-long-weekday-vector n))
  966.  
  967. (define (priv:locale-abbr-month n)
  968.   (vector-ref priv:locale-abbr-month-vector n))
  969.  
  970. (define (priv:locale-long-month n)
  971.   (vector-ref priv:locale-long-month-vector n))
  972.  
  973. (define (priv:vector-find needle haystack comparator)
  974.   (let ((len (vector-length haystack)))
  975.     (define (priv:vector-find-int index)
  976.       (cond
  977.        ((>= index len) #f)
  978.        ((comparator needle (vector-ref haystack index)) index)
  979.        (else (priv:vector-find-int (+ index 1)))))
  980.     (priv:vector-find-int 0)))
  981.  
  982. (define (priv:locale-abbr-weekday->index string)
  983.   (priv:vector-find string priv:locale-abbr-weekday-vector string=?))
  984.  
  985. (define (priv:locale-long-weekday->index string)
  986.   (priv:vector-find string priv:locale-long-weekday-vector string=?))
  987.  
  988. (define (priv:locale-abbr-month->index string)
  989.   (priv:vector-find string priv:locale-abbr-month-vector string=?))
  990.  
  991. (define (priv:locale-long-month->index string)
  992.   (priv:vector-find string priv:locale-long-month-vector string=?))
  993.  
  994.  
  995. ;; FIXME: mkoeppe: Put a symbolic time zone in the date structs.
  996. ;; Print it here instead of the numerical offset if available.
  997. (define (priv:locale-print-time-zone date port)
  998.   (priv:tz-printer (date-zone-offset date) port))
  999.  
  1000. ;; FIXME: we should use strftime to determine this dynamically if possible.
  1001. ;; Again, locale specific.
  1002. (define (priv:locale-am/pm hr)
  1003.   (if (> hr 11) priv:locale-pm priv:locale-am))
  1004.  
  1005. (define (priv:tz-printer offset port)
  1006.   (cond
  1007.    ((= offset 0) (display "Z" port))
  1008.    ((negative? offset) (display "-" port))
  1009.    (else (display "+" port)))
  1010.   (if (not (= offset 0))
  1011.       (let ((hours   (abs (quotient offset (* 60 60))))
  1012.             (minutes (abs (quotient (remainder offset (* 60 60)) 60))))
  1013.         (display (priv:padding hours #\0 2) port)
  1014.         (display (priv:padding minutes #\0 2) port))))
  1015.  
  1016. ;; A table of output formatting directives.
  1017. ;; the first time is the format char.
  1018. ;; the second is a procedure that takes the date, a padding character
  1019. ;; (which might be #f), and the output port.
  1020. ;;
  1021. (define priv:directives
  1022.   (list
  1023.    (cons #\~ (lambda (date pad-with port)
  1024.                (display #\~ port)))
  1025.    (cons #\a (lambda (date pad-with port)
  1026.                (display (priv:locale-abbr-weekday (date-week-day date))
  1027.                         port)))
  1028.    (cons #\A (lambda (date pad-with port)
  1029.                (display (priv:locale-long-weekday (date-week-day date))
  1030.                         port)))
  1031.    (cons #\b (lambda (date pad-with port)
  1032.                (display (priv:locale-abbr-month (date-month date))
  1033.                         port)))
  1034.    (cons #\B (lambda (date pad-with port)
  1035.                (display (priv:locale-long-month (date-month date))
  1036.                         port)))
  1037.    (cons #\c (lambda (date pad-with port)
  1038.                (display (date->string date priv:locale-date-time-format) port)))
  1039.    (cons #\d (lambda (date pad-with port)
  1040.                (display (priv:padding (date-day date)
  1041.                                       #\0 2)
  1042.                         port)))
  1043.    (cons #\D (lambda (date pad-with port)
  1044.                (display (date->string date "~m/~d/~y") port)))
  1045.    (cons #\e (lambda (date pad-with port)
  1046.                (display (priv:padding (date-day date)
  1047.                                       #\Space 2)
  1048.                         port)))
  1049.    (cons #\f (lambda (date pad-with port)
  1050.                (if (> (date-nanosecond date)
  1051.                       priv:nano)
  1052.                    (display (priv:padding (+ (date-second date) 1)
  1053.                                           pad-with 2)
  1054.                             port)
  1055.                    (display (priv:padding (date-second date)
  1056.                                           pad-with 2)
  1057.                             port))
  1058.                (receive (i f)
  1059.                         (priv:split-real (/
  1060.                                           (date-nanosecond date)
  1061.                                           priv:nano 1.0))
  1062.                         (let* ((ns (number->string f))
  1063.                                (le (string-length ns)))
  1064.                           (if (> le 2)
  1065.                               (begin
  1066.                                 (display priv:locale-number-separator port)
  1067.                                 (display (substring ns 2 le) port)))))))
  1068.    (cons #\h (lambda (date pad-with port)
  1069.                (display (date->string date "~b") port)))
  1070.    (cons #\H (lambda (date pad-with port)
  1071.                (display (priv:padding (date-hour date)
  1072.                                       pad-with 2)
  1073.                         port)))
  1074.    (cons #\I (lambda (date pad-with port)
  1075.                (let ((hr (date-hour date)))
  1076.                  (if (> hr 12)
  1077.                      (display (priv:padding (- hr 12)
  1078.                                             pad-with 2)
  1079.                               port)
  1080.                      (display (priv:padding hr
  1081.                                             pad-with 2)
  1082.                               port)))))
  1083.    (cons #\j (lambda (date pad-with port)
  1084.                (display (priv:padding (date-year-day date)
  1085.                                       pad-with 3)
  1086.                         port)))
  1087.    (cons #\k (lambda (date pad-with port)
  1088.                (display (priv:padding (date-hour date)
  1089.                                       #\Space 2)
  1090.                         port)))
  1091.    (cons #\l (lambda (date pad-with port)
  1092.                (let ((hr (if (> (date-hour date) 12)
  1093.                              (- (date-hour date) 12) (date-hour date))))
  1094.                  (display (priv:padding hr  #\Space 2)
  1095.                           port))))
  1096.    (cons #\m (lambda (date pad-with port)
  1097.                (display (priv:padding (date-month date)
  1098.                                       pad-with 2)
  1099.                         port)))
  1100.    (cons #\M (lambda (date pad-with port)
  1101.                (display (priv:padding (date-minute date)
  1102.                                       pad-with 2)
  1103.                         port)))
  1104.    (cons #\n (lambda (date pad-with port)
  1105.                (newline port)))
  1106.    (cons #\N (lambda (date pad-with port)
  1107.                (display (priv:padding (date-nanosecond date)
  1108.                                       pad-with 7)
  1109.                         port)))
  1110.    (cons #\p (lambda (date pad-with port)
  1111.                (display (priv:locale-am/pm (date-hour date)) port)))
  1112.    (cons #\r (lambda (date pad-with port)
  1113.                (display (date->string date "~I:~M:~S ~p") port)))
  1114.    (cons #\s (lambda (date pad-with port)
  1115.                (display (time-second (date->time-utc date)) port)))
  1116.    (cons #\S (lambda (date pad-with port)
  1117.                (if (> (date-nanosecond date)
  1118.                       priv:nano)
  1119.                    (display (priv:padding (+ (date-second date) 1)
  1120.                                           pad-with 2)
  1121.                             port)
  1122.                    (display (priv:padding (date-second date)
  1123.                                           pad-with 2)
  1124.                             port))))
  1125.    (cons #\t (lambda (date pad-with port)
  1126.                (display #\Tab port)))
  1127.    (cons #\T (lambda (date pad-with port)
  1128.                (display (date->string date "~H:~M:~S") port)))
  1129.    (cons #\U (lambda (date pad-with port)
  1130.                (if (> (priv:days-before-first-week date 0) 0)
  1131.                    (display (priv:padding (+ (date-week-number date 0) 1)
  1132.                                           #\0 2) port)
  1133.                    (display (priv:padding (date-week-number date 0)
  1134.                                           #\0 2) port))))
  1135.    (cons #\V (lambda (date pad-with port)
  1136.                (display (priv:padding (date-week-number date 1)
  1137.                                       #\0 2) port)))
  1138.    (cons #\w (lambda (date pad-with port)
  1139.                (display (date-week-day date) port)))
  1140.    (cons #\x (lambda (date pad-with port)
  1141.                (display (date->string date priv:locale-short-date-format) port)))
  1142.    (cons #\X (lambda (date pad-with port)
  1143.                (display (date->string date priv:locale-time-format) port)))
  1144.    (cons #\W (lambda (date pad-with port)
  1145.                (if (> (priv:days-before-first-week date 1) 0)
  1146.                    (display (priv:padding (+ (date-week-number date 1) 1)
  1147.                                           #\0 2) port)
  1148.                    (display (priv:padding (date-week-number date 1)
  1149.                                           #\0 2) port))))
  1150.    (cons #\y (lambda (date pad-with port)
  1151.                (display (priv:padding (priv:last-n-digits
  1152.                                        (date-year date) 2)
  1153.                                       pad-with
  1154.                                       2)
  1155.                         port)))
  1156.    (cons #\Y (lambda (date pad-with port)
  1157.                (display (date-year date) port)))
  1158.    (cons #\z (lambda (date pad-with port)
  1159.                (priv:tz-printer (date-zone-offset date) port)))
  1160.    (cons #\Z (lambda (date pad-with port)
  1161.                (priv:locale-print-time-zone date port)))
  1162.    (cons #\1 (lambda (date pad-with port)
  1163.                (display (date->string date "~Y-~m-~d") port)))
  1164.    (cons #\2 (lambda (date pad-with port)
  1165.                (display (date->string date "~k:~M:~S~z") port)))
  1166.    (cons #\3 (lambda (date pad-with port)
  1167.                (display (date->string date "~k:~M:~S") port)))
  1168.    (cons #\4 (lambda (date pad-with port)
  1169.                (display (date->string date "~Y-~m-~dT~k:~M:~S~z") port)))
  1170.    (cons #\5 (lambda (date pad-with port)
  1171.                (display (date->string date "~Y-~m-~dT~k:~M:~S") port)))))
  1172.  
  1173.  
  1174. (define (priv:get-formatter char)
  1175.   (let ((associated (assoc char priv:directives)))
  1176.     (if associated (cdr associated) #f)))
  1177.  
  1178. (define (priv:date-printer date index format-string str-len port)
  1179.   (if (>= index str-len)
  1180.       (values)
  1181.       (let ((current-char (string-ref format-string index)))
  1182.         (if (not (char=? current-char #\~))
  1183.             (begin
  1184.               (display current-char port)
  1185.               (priv:date-printer date (+ index 1) format-string str-len port))
  1186.             (if (= (+ index 1) str-len) ; bad format string.
  1187.                 (priv:time-error 'priv:date-printer 'bad-date-format-string
  1188.                                  format-string)
  1189.                 (let ((pad-char? (string-ref format-string (+ index 1))))
  1190.                   (cond
  1191.                    ((char=? pad-char? #\-)
  1192.                     (if (= (+ index 2) str-len) ; bad format string.
  1193.                         (priv:time-error 'priv:date-printer
  1194.                                          'bad-date-format-string
  1195.                                          format-string)
  1196.                         (let ((formatter (priv:get-formatter
  1197.                                           (string-ref format-string
  1198.                                                       (+ index 2)))))
  1199.                           (if (not formatter)
  1200.                               (priv:time-error 'priv:date-printer
  1201.                                                'bad-date-format-string
  1202.                                                format-string)
  1203.                               (begin
  1204.                                 (formatter date #f port)
  1205.                                 (priv:date-printer date
  1206.                                                    (+ index 3)
  1207.                                                    format-string
  1208.                                                    str-len
  1209.                                                    port))))))
  1210.  
  1211.                    ((char=? pad-char? #\_)
  1212.                     (if (= (+ index 2) str-len) ; bad format string.
  1213.                         (priv:time-error 'priv:date-printer
  1214.                                          'bad-date-format-string
  1215.                                          format-string)
  1216.                         (let ((formatter (priv:get-formatter
  1217.                                           (string-ref format-string
  1218.                                                       (+ index 2)))))
  1219.                           (if (not formatter)
  1220.                               (priv:time-error 'priv:date-printer
  1221.                                                'bad-date-format-string
  1222.                                                format-string)
  1223.                               (begin
  1224.                                 (formatter date #\Space port)
  1225.                                 (priv:date-printer date
  1226.                                                    (+ index 3)
  1227.                                                    format-string
  1228.                                                    str-len
  1229.                                                    port))))))
  1230.                    (else
  1231.                     (let ((formatter (priv:get-formatter
  1232.                                       (string-ref format-string
  1233.                                                   (+ index 1)))))
  1234.                       (if (not formatter)
  1235.                           (priv:time-error 'priv:date-printer
  1236.                                            'bad-date-format-string
  1237.                                            format-string)
  1238.                           (begin
  1239.                             (formatter date #\0 port)
  1240.                             (priv:date-printer date
  1241.                                                (+ index 2)
  1242.                                                format-string
  1243.                                                str-len
  1244.                                                port))))))))))))
  1245.  
  1246.  
  1247. (define (date->string date .  format-string)
  1248.   (let ((str-port (open-output-string))
  1249.         (fmt-str (if (null? format-string) "~c" (car format-string))))
  1250.     (priv:date-printer date 0 fmt-str (string-length fmt-str) str-port)
  1251.     (get-output-string str-port)))
  1252.  
  1253. (define (priv:char->int ch)
  1254.   (case ch
  1255.    ((#\0) 0)
  1256.    ((#\1) 1)
  1257.    ((#\2) 2)
  1258.    ((#\3) 3)
  1259.    ((#\4) 4)
  1260.    ((#\5) 5)
  1261.    ((#\6) 6)
  1262.    ((#\7) 7)
  1263.    ((#\8) 8)
  1264.    ((#\9) 9)
  1265.    (else (priv:time-error 'bad-date-template-string
  1266.                           (list "Non-integer character" ch i)))))
  1267.  
  1268. ;; read an integer upto n characters long on port; upto -> #f is any length
  1269. (define (priv:integer-reader upto port)
  1270.   (let loop ((accum 0) (nchars 0))
  1271.     (let ((ch (peek-char port)))
  1272.       (if (or (eof-object? ch)
  1273.               (not (char-numeric? ch))
  1274.               (and upto (>= nchars  upto)))
  1275.           accum
  1276.           (loop (+ (* accum 10) (priv:char->int (read-char port)))
  1277.                 (+ nchars 1))))))
  1278.  
  1279. (define (priv:make-integer-reader upto)
  1280.   (lambda (port)
  1281.     (priv:integer-reader upto port)))
  1282.  
  1283. ;; read *exactly* n characters and convert to integer; could be padded
  1284. (define (priv:integer-reader-exact n port)
  1285.   (let ((padding-ok #t))
  1286.     (define (accum-int port accum nchars)
  1287.       (let ((ch (peek-char port)))
  1288.     (cond
  1289.      ((>= nchars n) accum)
  1290.      ((eof-object? ch)
  1291.       (priv:time-error 'string->date 'bad-date-template-string
  1292.                            "Premature ending to integer read."))
  1293.      ((char-numeric? ch)
  1294.       (set! padding-ok #f)
  1295.       (accum-int port
  1296.                      (+ (* accum 10) (priv:char->int (read-char port)))
  1297.              (+ nchars 1)))
  1298.      (padding-ok
  1299.       (read-char port) ; consume padding
  1300.       (accum-int port accum (+ nchars 1)))
  1301.      (else ; padding where it shouldn't be
  1302.       (priv:time-error 'string->date 'bad-date-template-string
  1303.                            "Non-numeric characters in integer read.")))))
  1304.     (accum-int port 0 0)))
  1305.  
  1306.  
  1307. (define (priv:make-integer-exact-reader n)
  1308.   (lambda (port)
  1309.     (priv:integer-reader-exact n port)))
  1310.  
  1311. (define (priv:zone-reader port)
  1312.   (let ((offset 0)
  1313.         (positive? #f))
  1314.     (let ((ch (read-char port)))
  1315.       (if (eof-object? ch)
  1316.           (priv:time-error 'string->date 'bad-date-template-string
  1317.                            (list "Invalid time zone +/-" ch)))
  1318.       (if (or (char=? ch #\Z) (char=? ch #\z))
  1319.           0
  1320.           (begin
  1321.             (cond
  1322.              ((char=? ch #\+) (set! positive? #t))
  1323.              ((char=? ch #\-) (set! positive? #f))
  1324.              (else
  1325.               (priv:time-error 'string->date 'bad-date-template-string
  1326.                                (list "Invalid time zone +/-" ch))))
  1327.             (let ((ch (read-char port)))
  1328.               (if (eof-object? ch)
  1329.                   (priv:time-error 'string->date 'bad-date-template-string
  1330.                                    (list "Invalid time zone number" ch)))
  1331.               (set! offset (* (priv:char->int ch)
  1332.                               10 60 60)))
  1333.             (let ((ch (read-char port)))
  1334.               (if (eof-object? ch)
  1335.                   (priv:time-error 'string->date 'bad-date-template-string
  1336.                                    (list "Invalid time zone number" ch)))
  1337.               (set! offset (+ offset (* (priv:char->int ch)
  1338.                                         60 60))))
  1339.             (let ((ch (read-char port)))
  1340.               (if (eof-object? ch)
  1341.                   (priv:time-error 'string->date 'bad-date-template-string
  1342.                                    (list "Invalid time zone number" ch)))
  1343.               (set! offset (+ offset (* (priv:char->int ch)
  1344.                                         10 60))))
  1345.             (let ((ch (read-char port)))
  1346.               (if (eof-object? ch)
  1347.                   (priv:time-error 'string->date 'bad-date-template-string
  1348.                                    (list "Invalid time zone number" ch)))
  1349.               (set! offset (+ offset (* (priv:char->int ch)
  1350.                                         60))))
  1351.             (if positive? offset (- offset)))))))
  1352.  
  1353. ;; looking at a char, read the char string, run thru indexer, return index
  1354. (define (priv:locale-reader port indexer)
  1355.  
  1356.   (define (read-char-string result)
  1357.     (let ((ch (peek-char port)))
  1358.       (if (char-alphabetic? ch)
  1359.           (read-char-string (cons (read-char port) result))
  1360.           (list->string (reverse! result)))))
  1361.  
  1362.   (let* ((str (read-char-string '()))
  1363.          (index (indexer str)))
  1364.     (if index index (priv:time-error 'string->date
  1365.                                      'bad-date-template-string
  1366.                                      (list "Invalid string for " indexer)))))
  1367.  
  1368. (define (priv:make-locale-reader indexer)
  1369.   (lambda (port)
  1370.     (priv:locale-reader port indexer)))
  1371.  
  1372. (define (priv:make-char-id-reader char)
  1373.   (lambda (port)
  1374.     (if (char=? char (read-char port))
  1375.         char
  1376.         (priv:time-error 'string->date
  1377.                          'bad-date-template-string
  1378.                          "Invalid character match."))))
  1379.  
  1380. ;; A List of formatted read directives.
  1381. ;; Each entry is a list.
  1382. ;; 1. the character directive;
  1383. ;; a procedure, which takes a character as input & returns
  1384. ;; 2. #t as soon as a character on the input port is acceptable
  1385. ;; for input,
  1386. ;; 3. a port reader procedure that knows how to read the current port
  1387. ;; for a value. Its one parameter is the port.
  1388. ;; 4. a action procedure, that takes the value (from 3.) and some
  1389. ;; object (here, always the date) and (probably) side-effects it.
  1390. ;; In some cases (e.g., ~A) the action is to do nothing
  1391.  
  1392. (define priv:read-directives
  1393.   (let ((ireader4 (priv:make-integer-reader 4))
  1394.         (ireader2 (priv:make-integer-reader 2))
  1395.         (ireaderf (priv:make-integer-reader #f))
  1396.         (eireader2 (priv:make-integer-exact-reader 2))
  1397.         (eireader4 (priv:make-integer-exact-reader 4))
  1398.         (locale-reader-abbr-weekday (priv:make-locale-reader
  1399.                                      priv:locale-abbr-weekday->index))
  1400.         (locale-reader-long-weekday (priv:make-locale-reader
  1401.                                      priv:locale-long-weekday->index))
  1402.         (locale-reader-abbr-month   (priv:make-locale-reader
  1403.                                      priv:locale-abbr-month->index))
  1404.         (locale-reader-long-month   (priv:make-locale-reader
  1405.                                      priv:locale-long-month->index))
  1406.         (char-fail (lambda (ch) #t))
  1407.         (do-nothing (lambda (val object) (values))))
  1408.  
  1409.     (list
  1410.      (list #\~ char-fail (priv:make-char-id-reader #\~) do-nothing)
  1411.      (list #\a char-alphabetic? locale-reader-abbr-weekday do-nothing)
  1412.      (list #\A char-alphabetic? locale-reader-long-weekday do-nothing)
  1413.      (list #\b char-alphabetic? locale-reader-abbr-month
  1414.            (lambda (val object)
  1415.              (set-date-month! object val)))
  1416.      (list #\B char-alphabetic? locale-reader-long-month
  1417.            (lambda (val object)
  1418.              (set-date-month! object val)))
  1419.      (list #\d char-numeric? ireader2 (lambda (val object)
  1420.                                         (set-date-day!
  1421.                                          object val)))
  1422.      (list #\e char-fail eireader2 (lambda (val object)
  1423.                                      (set-date-day! object val)))
  1424.      (list #\h char-alphabetic? locale-reader-abbr-month
  1425.            (lambda (val object)
  1426.              (set-date-month! object val)))
  1427.      (list #\H char-numeric? ireader2 (lambda (val object)
  1428.                                         (set-date-hour! object val)))
  1429.      (list #\k char-fail eireader2 (lambda (val object)
  1430.                                      (set-date-hour! object val)))
  1431.      (list #\m char-numeric? ireader2 (lambda (val object)
  1432.                                         (set-date-month! object val)))
  1433.      (list #\M char-numeric? ireader2 (lambda (val object)
  1434.                                         (set-date-minute!
  1435.                                          object val)))
  1436.      (list #\S char-numeric? ireader2 (lambda (val object)
  1437.                                         (set-date-second! object val)))
  1438.      (list #\y char-fail eireader2
  1439.            (lambda (val object)
  1440.              (set-date-year! object (priv:natural-year val))))
  1441.      (list #\Y char-numeric? ireader4 (lambda (val object)
  1442.                                         (set-date-year! object val)))
  1443.      (list #\z (lambda (c)
  1444.                  (or (char=? c #\Z)
  1445.                      (char=? c #\z)
  1446.                      (char=? c #\+)
  1447.                      (char=? c #\-)))
  1448.            priv:zone-reader (lambda (val object)
  1449.                               (set-date-zone-offset! object val))))))
  1450.  
  1451. (define (priv:string->date date index format-string str-len port template-string)
  1452.   (define (skip-until port skipper)
  1453.     (let ((ch (peek-char port)))
  1454.       (if (eof-object? port)
  1455.           (priv:time-error 'string->date 'bad-date-format-string template-string)
  1456.           (if (not (skipper ch))
  1457.               (begin (read-char port) (skip-until port skipper))))))
  1458.   (if (>= index str-len)
  1459.       (begin
  1460.         (values))
  1461.       (let ((current-char (string-ref format-string index)))
  1462.         (if (not (char=? current-char #\~))
  1463.             (let ((port-char (read-char port)))
  1464.               (if (or (eof-object? port-char)
  1465.                       (not (char=? current-char port-char)))
  1466.                   (priv:time-error 'string->date
  1467.                                    'bad-date-format-string template-string))
  1468.               (priv:string->date date
  1469.                                  (+ index 1)
  1470.                                  format-string
  1471.                                  str-len
  1472.                                  port
  1473.                                  template-string))
  1474.             ;; otherwise, it's an escape, we hope
  1475.             (if (> (+ index 1) str-len)
  1476.                 (priv:time-error 'string->date
  1477.                                  'bad-date-format-string template-string)
  1478.                 (let* ((format-char (string-ref format-string (+ index 1)))
  1479.                        (format-info (assoc format-char priv:read-directives)))
  1480.                   (if (not format-info)
  1481.                       (priv:time-error 'string->date
  1482.                                        'bad-date-format-string template-string)
  1483.                       (begin
  1484.                         (let ((skipper (cadr format-info))
  1485.                               (reader  (caddr format-info))
  1486.                               (actor   (cadddr format-info)))
  1487.                           (skip-until port skipper)
  1488.                           (let ((val (reader port)))
  1489.                             (if (eof-object? val)
  1490.                                 (priv:time-error 'string->date
  1491.                                                  'bad-date-format-string
  1492.                                                  template-string)
  1493.                                 (actor val date)))
  1494.                           (priv:string->date date
  1495.                                              (+ index 2)
  1496.                                              format-string
  1497.                                              str-len
  1498.                                              port
  1499.                                              template-string))))))))))
  1500.  
  1501. (define (string->date input-string template-string)
  1502.   (define (priv:date-ok? date)
  1503.     (and (date-nanosecond date)
  1504.          (date-second date)
  1505.          (date-minute date)
  1506.          (date-hour date)
  1507.          (date-day date)
  1508.          (date-month date)
  1509.          (date-year date)
  1510.          (date-zone-offset date)))
  1511.   (let ((newdate (make-date 0 0 0 0 #f #f #f #f)))
  1512.     (priv:string->date newdate
  1513.                        0
  1514.                        template-string
  1515.                        (string-length template-string)
  1516.                        (open-input-string input-string)
  1517.                        template-string)
  1518.     (if (not (date-zone-offset newdate))
  1519.     (begin
  1520.       ;; this is necessary to get DST right -- as far as we can
  1521.       ;; get it right (think of the double/missing hour in the
  1522.       ;; night when we are switching between normal time and DST).
  1523.       (set-date-zone-offset! newdate
  1524.                  (priv:local-tz-offset
  1525.                   (make-time time-utc 0 0)))
  1526.       (set-date-zone-offset! newdate
  1527.                  (priv:local-tz-offset
  1528.                   (date->time-utc newdate)))))
  1529.     (if (priv:date-ok? newdate)
  1530.         newdate
  1531.         (priv:time-error
  1532.          'string->date
  1533.          'bad-date-format-string
  1534.          (list "Incomplete date read. " newdate template-string)))))
  1535.  
  1536. ;;; srfi-19.scm ends here
  1537.